home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Amiga Tools 2
/
Amiga Tools 2.iso
/
tools
/
jade
/
src
/
commands.c
< prev
next >
Wrap
C/C++ Source or Header
|
1995-03-09
|
16KB
|
575 lines
/* commands.c -- Interactive calling of commands/functions
Copyright (C) 1994 John Harper <jsh@ukc.ac.uk>
This file is part of Jade.
Jade is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.
Jade is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Jade; see the file COPYING. If not, write to
the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
#include "jade.h"
#include "jade_protos.h"
#include <string.h>
_PR void commands_init(void);
/* Symbols of the Lisp functions called to get input. */
static VALUE sym_prompt_for_function, sym_prompt_for_buffer,
sym_prompt_for_char, sym_prompt_for_command, sym_prompt_for_directory,
sym_prompt_for_file, sym_prompt_for_number, sym_prompt_for_string,
sym_prompt_for_symbol, sym_prompt_for_variable, sym_prompt_for_lisp,
sym_read_event;
static VALUE sym_interactive;
/* Prefix argument for the next command and the current command. */
static VALUE prefix_arg, current_prefix_arg;
/* Command being executed and command last executed. */
_PR VALUE this_command, last_command;
VALUE this_command, last_command;
_PR VALUE var_this_command(VALUE val);
DEFUN("this-command", var_this_command, subr_this_command, (VALUE val), V_Var, DOC_this_command) /*
::doc:this_command::
This variable holds the command currently being evaluated, or nil if no
command is active. The `command' is whatever is being evaluated; it could
be a function, a form or even a list of forms (from a menu).
::end:: */
{
if(val)
this_command = val;
return(this_command);
}
_PR VALUE var_last_command(VALUE val);
DEFUN("last-command", var_last_command, subr_last_command, (VALUE val), V_Var, DOC_last_command) /*
::doc:last_command::
This variable holds the last interactive command evaluated. This will either
be from a keybinding or a menu. Setting the value of the `next-keymap-path'
variable is not considered a command. After a command finishes this variable
takes the value of `this-command'.
::end:: */
{
if(val)
last_command = val;
return(last_command);
}
_PR VALUE var_prefix_arg(VALUE val);
DEFUN("prefix-arg", var_prefix_arg, subr_prefix_arg, (VALUE val), V_Var, DOC_prefix_arg) /*
::doc:prefix_arg::
Value of the prefix argument for the next command.
::end:: */
{
if(val)
prefix_arg = val;
return(prefix_arg);
}
_PR VALUE var_current_prefix_arg(VALUE val);
DEFUN("current-prefix-arg", var_current_prefix_arg, subr_current_prefix_arg, (VALUE val), V_Var, DOC_current_prefix_arg) /*
::doc:current_prefix_arg::
Value of the prefix argument for the current command.
::end:: */
{
if(val)
current_prefix_arg = val;
return(current_prefix_arg);
}
/* Search the definition of the command CMD for an interactive calling
spec. Return it or NULL. */
static VALUE
interactive_spec(VALUE cmd)
{
VALUE fun, spec = NULL;
if(SYMBOLP(cmd))
fun = cmd_symbol_function(cmd, sym_t);
else
fun = cmd;
if(!VOIDP(fun) && !NILP(fun))
{
if((VTYPE(fun) >= V_Subr0) && (VTYPE(fun) <= V_SubrN))
spec = VSUBR(fun)->subr_IntSpec;
else if(CONSP(fun))
{
if(VCAR(fun) == sym_autoload)
{
VALUE tmp = move_down_list(fun, 2);
if(CONSP(tmp) && !NILP(VCAR(tmp)))
{
GCVAL gcv_cmd;
PUSHGC(gcv_cmd, cmd);
fun = load_autoload(cmd, fun);
POPGC;
if(!fun || !CONSP(fun))
return(NULL);
}
else
return(NULL);
}
if(VCAR(fun) == sym_lambda)
{
/* A lambda expression, test its first proper form. */
fun = move_down_list(fun, 2);
if(CONSP(fun)
&& (STRINGP(VCAR(fun)) || NUMBERP(VCAR(fun)))
&& CONSP(VCDR(fun)))
{
/* A doc-string */
fun = VCDR(fun);
}
if(CONSP(fun))
{
fun = VCAR(fun);
if(CONSP(fun)
&& (VCAR(fun) == sym_interactive))
{
/* got it. */
spec = CONSP(VCDR(fun)) ? VCAR(VCDR(fun)) : sym_nil;
}
}
}
}
}
return(spec);
}
_PR VALUE cmd_call_command(VALUE cmd, VALUE arg);
DEFUN_INT("call-command", cmd_call_command, subr_call_command, (VALUE cmd, VALUE cmd_arg), V_Subr2, DOC_call_command, "CEnter command:\nP") /*
::doc:call_command::
call-command COMMAND [PREFIX-ARG]
Invoke the command COMMAND. This can be one of,
1. A symbol whose function value is to be called, the symbol must be of
type `commandp'; any interactive calling specification will be
used to find arguments to give to the function. (see `interactive')
2. A lambda-expression to call as a function name
3. A single Lisp form to be evaluated by eval
If PREFIX-ARG is non-nil it specifies the value of the COMMAND's
current-prefix-arg. This is used in call-command's interactive spec so that
any entered arg is given to the invoked COMMAND.
::end:: */
{
VALUE res = NULL;
this_command = cmd;
if(last_command == sym_t)
undo_distinct(); /* last was an insertion */
undo_new_group();
/* Move the prefix arg. */
if(NILP(cmd_arg))
cmd_arg = prefix_arg;
prefix_arg = sym_nil;
current_prefix_arg = cmd_arg;
if(SYMBOLP(cmd) || (CONSP(cmd) && VCAR(cmd) == sym_lambda))
{
/* A named command; call it properly taking note of any interactive
declaration. */
VALUE int_spec = interactive_spec(cmd);
VALUE args = sym_nil;
VALUE *argsp = &args;
GCVAL gcv_cmd;
bool clear_block = FALSE;
if(int_spec == NULL)
{
cmd_signal(sym_error, list_2(MKSTR("Not a command"), cmd));
goto exit;
}
PUSHGC(gcv_cmd, cmd);
if(STRINGP(int_spec))
{
u_char *spec_str = VSTR(int_spec);
u_char c;
GCVAL gcv_args;
while(1)
{
/* check for read-only flag */
if(*spec_str == '*')
{
if(read_only(curr_vw->vw_Tx))
{
POPGC;
goto exit;
}
else
spec_str++;
}
else if(*spec_str == '-')
{
/* clear block after building args. */
clear_block = TRUE;
spec_str++;
}
else
break;
}
PUSHGC(gcv_args, args);
while((c = *spec_str++) != 0)
{
VALUE prompt, arg = sym_nil;
if(c != '\n')
{
/* Non-null code. */
bool can_be_nil = FALSE;
if(*spec_str == '\n')
{
/* no prompt */
prompt = sym_nil;
spec_str++;
}
else
{
/* copy the prompt */
u_char *end = memchr(spec_str, '\n',
STRING_LEN(int_spec) -
(spec_str - VSTR(int_spec)));
if(!end)
end = VSTR(int_spec) + STRING_LEN(int_spec);
prompt = string_dupn(spec_str, end - spec_str);
if(memchr(spec_str, '%', end - spec_str))
{
/* Format characters; format it. */
prompt = cmd_format(cmd_cons(sym_nil,
cmd_cons(prompt, args)));
if(!prompt || !STRINGP(prompt))
prompt = string_dupn(spec_str, end - spec_str);
}
spec_str = *end ? end + 1 : end;
}
switch(c)
{
case 'a':
arg = call_lisp1(sym_prompt_for_function, prompt);
break;
case 'b':
arg = call_lisp2(sym_prompt_for_buffer, prompt, sym_t);
break;
case 'B':
arg = call_lisp1(sym_prompt_for_buffer, prompt);
break;
case 'c':
arg = call_lisp1(sym_prompt_for_char, prompt);
break;
case 'C':
arg = call_lisp1(sym_prompt_for_command, prompt);
break;
case 'd':
arg = cmd_cursor_pos();
break;
case 'D':
arg = call_lisp1(sym_prompt_for_directory, prompt);
break;
case 'e':
arg = cmd_current_event();
break;
case 'E':
arg = cmd_current_event_string();
break;
case 'f':
arg = call_lisp2(sym_prompt_for_file, prompt, sym_t);
break;
case 'F':
arg = call_lisp1(sym_prompt_for_file, prompt);
break;
case 'k':
arg = call_lisp1(sym_read_event, prompt);
break;
case 'm':
case 'M':
arg = (c == 'm') ? cmd_block_start(sym_nil)
: cmd_block_end(sym_nil);
if(!arg || NILP(arg))
{
arg = NULL;
cmd_signal(sym_error,
LIST_1(MKSTR("No block marked")));
}
break;
case 'n':
arg = call_lisp1(sym_prompt_for_number, prompt);
break;
case 'N':
if(NILP(cmd_arg))
arg = call_lisp1(sym_prompt_for_number, prompt);
else
arg = cmd_prefix_numeric_argument(cmd_arg);
break;
case 'p':
arg = cmd_prefix_numeric_argument(cmd_arg);
break;
case 'P':
arg = cmd_arg;
can_be_nil = TRUE;
break;
case 's':
arg = call_lisp1(sym_prompt_for_string, prompt);
break;
case 'S':
arg = call_lisp1(sym_prompt_for_symbol, prompt);
can_be_nil = TRUE;
break;
case 't':
arg = sym_t;
break;
case 'v':
arg = call_lisp1(sym_prompt_for_variable, prompt);
break;
case 'x':
arg = call_lisp1(sym_prompt_for_lisp, prompt);
can_be_nil = TRUE;
break;
case 'X':
arg = call_lisp1(sym_prompt_for_lisp, prompt);
if(arg)
arg = cmd_eval(arg);
can_be_nil = TRUE;
break;
default:
arg = NULL;
cmd_signal(sym_interactive, list_2(cmd, int_spec));
}
if(!arg)
{
args = NULL;
break;
}
if(!can_be_nil && NILP(arg))
{
cmd_signal(sym_error,
list_2(MKSTR("Nil argument to command"),
cmd));
args = NULL;
break;
}
}
/* Tack on this argument. */
*argsp = cmd_cons(arg, sym_nil);
argsp = &VCDR(*argsp);
}
POPGC;
}
else if(int_spec != sym_t)
args = cmd_eval(int_spec);
if(clear_block)
cmd_block_kill();
if(args)
res = funcall(cmd, args);
POPGC;
}
else
res = cmd_eval(cmd);
exit:
last_command = this_command;
/* This is in here so it can tell if the last binding was actually
a command. */
undo_distinct();
this_command = sym_nil;
current_prefix_arg = sym_nil;
return(res);
}
_PR VALUE cmd_prefix_numeric_argument(VALUE arg);
DEFUN("prefix-numeric-argument", cmd_prefix_numeric_argument, subr_prefix_numeric_argument, (VALUE arg), V_Subr1, DOC_prefix_numeric_argument) /*
::doc:prefix_numeric_argument::
prefix-numeric-argument ARG
Returns the numeric value of the raw prefix argument ARG.
::end:: */
{
switch(VTYPE(arg))
{
case V_Symbol:
arg = make_number(NILP(arg) ? 1 : -1);
break;
case V_Number:
break;
case V_Cons:
arg = VCAR(arg);
break;
default:
arg = make_number(1);
}
return(arg);
}
_PR VALUE cmd_interactive(VALUE spec);
DEFUN("interactive", cmd_interactive, subr_interactive, (VALUE arg_list), V_SF, DOC_interactive) /*
::doc:interactive::
interactive CALLING-SPEC
This is a declaration used by the `call-command' function. For each Lisp
function which may be invoked as a command (interactively by the user) the
first *actual* form of the function (after the arguments and optional doc
string) must be an `interactive' declaration. For example,
(defun foo (bar)
"An illustration"
(interactive ...)
...
When called, the interactive special form just returns nil.
The CALLING-SPEC defines the arguments which are given to the command, it
can be either,
1. nil -- no arguments are given to the function, this is just used to show
that this function may be called as a command.
2. A Lisp form -- it is evaluated and expected to provide a *list* of
arguments which will be given to the function
3. A string -- zero or more lines (separated by `\n'); each line tells
how to get one argument. The first character of each line is a code
letter, the rest of the line is an optional prompt-string which the
user will see when entering the argument's value.
The code letters available are,
a A function
b An existing buffer
B A buffer, it will be created if it doesn't exist
c A character
C A command
d The position of the cursor
D The name of a directory
e The event which caused this command
E The event which caused this command as a string
f The name of an existing file
F The name of a file
k An event
m The start position of the currently-marked block
M The end of the block
n A number
N The numeric prefix arg, or an entered number
p The numeric prefix arg
P The raw prefix arg
s A string
S A symbol
t The symbol `t'
v A variable
x A Lisp object
X A Lisp object, read then evaluated
A null line produces an argument of nil.
Any non-alphabetic characters at the beginning of the CALLING-SPEC
are used as flags, the currently recognised flags are,
* If the active buffer is read-only an error will be signalled
- After building the argument list the block marked in the
current window will be unmarked.
Example usage,
(interactive) -- No arguments but function may
be called as a command
(interactive "bBuffer to kill:") -- One arg, an existing buffer
(interactive "*\nxLisp form:\nt") -- If not read-only, three arguments;
`nil', a lisp form and `t'.
::end:: */
{
return(sym_nil);
}
_PR VALUE cmd_commandp(VALUE cmd);
DEFUN("commandp", cmd_commandp, subr_commandp, (VALUE cmd), V_Subr1, DOC_commandp) /*
::doc:commandp::
commandp COMMAND
Returns t if COMMAND may be called interactively.
::end:: */
{
if(SYMBOLP(cmd))
cmd = cmd_symbol_function(cmd, sym_t);
if(!VOIDP(cmd) && !NILP(cmd))
{
if(((VTYPE(cmd) >= V_Subr0) && (VTYPE(cmd) <= V_SubrN))
&& (VSUBR(cmd)->subr_IntSpec != NULL))
return(sym_t);
else if(CONSP(cmd))
{
if(VCAR(cmd) == sym_autoload)
{
cmd = find_member_by_index(cmd, 3);
if(!NILP(cmd))
return(sym_t);
}
else if(VCAR(cmd) == sym_lambda)
{
/* A lambda expression, test its first proper form. */
cmd = move_down_list(cmd, 2);
if(CONSP(cmd)
&& (STRINGP(VCAR(cmd)) || NUMBERP(VCAR(cmd)))
&& CONSP(VCDR(cmd)))
{
/* A doc-string */
cmd = VCDR(cmd);
}
if(CONSP(cmd))
{
cmd = VCAR(cmd);
if(CONSP(cmd)
&& (VCAR(cmd) == sym_interactive))
{
return(sym_t);
}
}
}
}
}
return(sym_nil);
}
void
commands_init(void)
{
/* Create the function symbols. */
INTERN(sym_prompt_for_function, "prompt-for-function");
INTERN(sym_prompt_for_buffer, "prompt-for-buffer");
INTERN(sym_prompt_for_char, "prompt-for-char");
INTERN(sym_prompt_for_command, "prompt-for-command");
INTERN(sym_prompt_for_directory, "prompt-for-directory");
INTERN(sym_prompt_for_file, "prompt-for-file");
INTERN(sym_prompt_for_number, "prompt-for-number");
INTERN(sym_prompt_for_string, "prompt-for-string");
INTERN(sym_prompt_for_symbol, "prompt-for-symbol");
INTERN(sym_prompt_for_variable, "prompt-for-variable");
INTERN(sym_prompt_for_lisp, "prompt-for-lisp");
INTERN(sym_read_event, "read-event");
INTERN(sym_interactive, "interactive");
cmd_put(sym_interactive, sym_error_message,
MKSTR("Bad interactive specification"));
prefix_arg = current_prefix_arg = sym_nil;
mark_static(&prefix_arg);
mark_static(¤t_prefix_arg);
this_command = last_command = sym_nil;
mark_static(&this_command);
mark_static(&last_command);
ADD_SUBR(subr_this_command);
ADD_SUBR(subr_last_command);
ADD_SUBR(subr_prefix_arg);
ADD_SUBR(subr_current_prefix_arg);
ADD_SUBR(subr_call_command);
ADD_SUBR(subr_prefix_numeric_argument);
ADD_SUBR(subr_interactive);
ADD_SUBR(subr_commandp);
}